using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Linq;
using System.Linq.Expressions;
using System.Threading.Tasks;
using Comteck.Domain.Entities;

namespace Comteck.Ktt.Services.Infrastructure {
  /// <summary>
  /// /
  /// </summary>
  /// <typeparam name="TEntity"></typeparam>
  /// <typeparam name="TKey"></typeparam>
  public class KttRepository<TEntity, TKey> : IKttRepository<TEntity, TKey> where TEntity : class, IEntity<TKey> {
    protected readonly DbContext _context;
    protected readonly DbSet<TEntity> _dbSet;

    public KttRepository(DbContext context) {
      _context = context ?? throw new ArgumentNullException(nameof(context));
      _dbSet = _context.Set<TEntity>();
    }

    /// <summary>
    /// 
    /// </summary>
    public DbContext DbContext => _context;
    //
    // 摘要:
    //     Returns the queryable entity set for the given type {T}.
    //
    // 返回结果:
    //     IQueryable to be used to select entities from database
    public IQueryable<TEntity> Table => _dbSet;

    //
    // 摘要:
    //     Returns an untracked queryable entity set for the given type {T}. The entities
    //     returned will not be cached in the object context thus increasing performance.
    //
    //
    // 返回结果:
    //     IQueryable to be used to select entities from database
    public IQueryable<TEntity> TableUntracked => _dbSet.AsNoTracking();

    public async Task<TEntity> GetByIdAsync(TKey id) {
      return await _dbSet.FindAsync(id);
    }

    public async Task<IEnumerable<TEntity>> GetAllAsync() {
      return await _dbSet.AsNoTracking().ToListAsync();
    }

    public async Task<IEnumerable<TEntity>> FindAsync(Expression<Func<TEntity, bool>> predicate) {
      return await _dbSet.Where(predicate).AsNoTracking().ToListAsync();
    }

    public async Task AddAsync(TEntity entity) {
      _dbSet.Add(entity);
      await _context.SaveChangesAsync();
    }

    public async Task AddRangeAsync(IEnumerable<TEntity> entities) {
      _dbSet.AddRange(entities);
      await _context.SaveChangesAsync();
    }

    public async Task UpdateAsync(TEntity entity) {
      AttachIfNot(entity);
      _context.Entry(entity).State = EntityState.Modified;
      await _context.SaveChangesAsync();
    }

    public async Task UpdateRangeAsync(IEnumerable<TEntity> entities) {
      foreach (var entity in entities) {
        AttachIfNot(entity);
        _context.Entry(entity).State = EntityState.Modified;
      }
      await _context.SaveChangesAsync();
    }

    public async Task RemoveAsync(TEntity entity) {
      AttachIfNot(entity);
      _context.Entry(entity).State = EntityState.Deleted;
      _dbSet.Remove(entity);
      await _context.SaveChangesAsync();
    }

    public async Task RemoveRangeAsync(IEnumerable<TEntity> entities) {
      foreach (var entity in entities) {
        AttachIfNot(entity);
        _context.Entry(entity).State = EntityState.Deleted;
      }

      _dbSet.RemoveRange(entities);
      await _context.SaveChangesAsync();
    }

    public async Task<int> CountAsync(Expression<Func<TEntity, bool>> predicate) {
      return await _dbSet.Where(predicate).CountAsync();
    }
    public async Task<bool> AnyAsync(Expression<Func<TEntity, bool>> predicate) {
      return await _dbSet.AnyAsync(predicate);
    }

    public async Task<int> SaveChangesAsync() {
      return await _context.SaveChangesAsync();
    }


    protected virtual void AttachIfNot(TEntity entity) {
      // 检查实体是否已被跟踪
      var existingEntity = _context.Set<TEntity>().Local
          .FirstOrDefault(e => e.Id.Equals(entity.Id));
      if (existingEntity != null) {
        _context.Entry(existingEntity).State = EntityState.Detached;
      }

      //var entry = this._context.ChangeTracker.Entries().FirstOrDefault(ent => ent.Entity.Equals(entity));
      //if (entry != null) {
      //  return;
      //}

      //this._dbSet.Attach(entity);
    }
  }
}
